package com.xiyuan.smartutils.reflect.internal;

import com.xiyuan.smartutils.Resources;
import com.xiyuan.smartutils.StrUtils;
import com.xiyuan.smartutils.Systems;
import com.xiyuan.smartutils.reflect.Reflect;
import com.xiyuan.smartutils.reflect.Unsafe;

import java.io.BufferedReader;
import java.io.StringReader;
import java.lang.Module;
import java.lang.ModuleLayer;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.security.ProtectionDomain;

/**
 * jdk9 之后的 Unsafe 访问器
 *
 * @author xiyuan-lgz 2025-03-16 @version v1.0.0 新建与整理
 */
@SuppressWarnings("unchecked")
public final class UnsafeImpl extends Unsafe {
    private static ModuleLayer moduleLayer;
    
    private UnsafeImpl(Object javaUnsafe) {
        super(javaUnsafe);
        moduleLayer = ModuleLayer.boot();
        Module[] modules = {
                this.getClass().getModule(),
                Systems.class.getModule(),
                Reflect.class.getModule(),
                };
        
        String string = Resources.getResourceStringUTF8(UnsafeImpl.class, "/META-INF/versions/9/opens.inf");
        if (string == null) {
            return;
        }
        
        new BufferedReader(new StringReader(string)).lines().forEach(line -> addOpensInner(line, modules));
    }
    
    
    private void addOpensInner(String params, Module[] targetModule) {
        if (StrUtils.isBlank(params)) {
            return;
        }
        
        Throwable ex = null;
        String[] ss = params.split("/");
        Module source = moduleLayer.findModule(ss[0]).orElse(null);
        if (source == null) return;
        for (int i = 0; i < targetModule.length; i++) {
            try {
                openInner(source, StrUtils.trim(ss[1]), targetModule[i]);
            }
            catch (Throwable e) {
                ex = e;
            }
        }
        
        if (ex != null) {
            ex.getCause();
        }
    }
    
    @Override
    public int getVersion() {
        return 9;
    }
    
    @Override
    protected Object getModule(Object obj) {
        if (obj instanceof Module) {
            return obj;
        }
        if (obj instanceof Class) {
            return ((Class<?>) obj).getModule();
        }
        if (obj instanceof Field) {
            return ((Field) obj).getDeclaringClass().getModule();
        }
        if (obj instanceof Method) {
            return ((Method) obj).getDeclaringClass().getModule();
        }
        return obj.getClass().getModule();
    }
    
    @Override
    public void putObject(Object obj, long offset, Object value) {
        ((jdk.internal.misc.Unsafe) junsafe).putObject(obj, offset, value);
    }
    
    @Override
    public Object getObject(Object obj, long offset) {
        return ((jdk.internal.misc.Unsafe) junsafe).getObject(obj, offset);
    }
    
    @Override
    public void putBoolean(Object obj, long offset, boolean value) {
        ((jdk.internal.misc.Unsafe) junsafe).putBoolean(obj, offset, value);
    }
    
    @Override
    public boolean getBoolean(Object obj, long offset) {
        return ((jdk.internal.misc.Unsafe) junsafe).getBoolean(obj, offset);
    }
    
    @Override
    public void putByte(Object obj, long offset, byte value) {
        ((jdk.internal.misc.Unsafe) junsafe).putByte(obj, offset, value);
    }
    
    @Override
    public byte getByte(Object obj, long offset) {
        return ((jdk.internal.misc.Unsafe) junsafe).getByte(obj, offset);
    }
    
    @Override
    public void putChar(Object obj, long offset, char value) {
        ((jdk.internal.misc.Unsafe) junsafe).putChar(obj, offset, value);
    }
    
    @Override
    public char getChar(Object obj, long offset) {
        return ((jdk.internal.misc.Unsafe) junsafe).getChar(obj, offset);
    }
    
    @Override
    public void putShort(Object obj, long offset, short value) {
        ((jdk.internal.misc.Unsafe) junsafe).putShort(obj, offset, value);
    }
    
    @Override
    public short getShort(Object obj, long offset) {
        return ((jdk.internal.misc.Unsafe) junsafe).getShort(obj, offset);
    }
    
    @Override
    public void putInt(Object obj, long offset, int value) {
        ((jdk.internal.misc.Unsafe) junsafe).putInt(obj, offset, value);
    }
    
    @Override
    public int getInt(Object obj, long offset) {
        return ((jdk.internal.misc.Unsafe) junsafe).getInt(obj, offset);
    }
    
    @Override
    public void putLong(Object obj, long offset, long value) {
        ((jdk.internal.misc.Unsafe) junsafe).putLong(obj, offset, value);
    }
    
    @Override
    public long getLong(Object obj, long offset) {
        return ((jdk.internal.misc.Unsafe) junsafe).getLong(obj, offset);
    }
    
    @Override
    public void putFloat(Object obj, long offset, float value) {
        ((jdk.internal.misc.Unsafe) junsafe).putFloat(obj, offset, value);
    }
    
    @Override
    public float getFloat(Object obj, long offset) {
        return ((jdk.internal.misc.Unsafe) junsafe).getFloat(obj, offset);
    }
    
    @Override
    public void putDouble(Object obj, long offset, double value) {
        ((jdk.internal.misc.Unsafe) junsafe).putDouble(obj, offset, value);
    }
    
    @Override
    public double getDouble(Object obj, long offset) {
        return ((jdk.internal.misc.Unsafe) junsafe).getDouble(obj, offset);
    }
    
    @Override
    public void freeMemory(long address) {
        ((jdk.internal.misc.Unsafe) junsafe).freeMemory(address);
    }
    
    @Override
    public long allocateMemory(long bytes) {
        return ((jdk.internal.misc.Unsafe) junsafe).allocateMemory(bytes);
    }
    
    @Override
    public long reallocateMemory(long address, long bytes) {
        return ((jdk.internal.misc.Unsafe) junsafe).reallocateMemory(address, bytes);
    }
    
    @Override
    public void setMemory(Object obj, long offset, long bytes, byte value) {
        ((jdk.internal.misc.Unsafe) junsafe).setMemory(obj, offset, bytes, value);
    }
    
    @Override
    public void copyMemory(Object srcBase, long srcOffset, Object destBase, long destOffset, long bytes) {
        ((jdk.internal.misc.Unsafe) junsafe).copyMemory(srcBase, srcOffset, destBase, destOffset, bytes);
    }
    
    @Override
    public int pageSize() {
        return ((jdk.internal.misc.Unsafe) junsafe).pageSize();
    }
    
    @Override
    public void unpark(Object thread) {
        ((jdk.internal.misc.Unsafe) junsafe).unpark(thread);
    }
    
    @Override
    public void park(boolean isAbsolute, long time) {
        ((jdk.internal.misc.Unsafe) junsafe).park(isAbsolute, time);
    }
    
    @Override
    public int getLoadAverage(double[] loadavg, int nelems) {
        return ((jdk.internal.misc.Unsafe) junsafe).getLoadAverage(loadavg, nelems);
    }
    
    @Override
    public void loadFence() {
        ((jdk.internal.misc.Unsafe) junsafe).loadFence();
    }
    
    @Override
    public void storeFence() {
        ((jdk.internal.misc.Unsafe) junsafe).storeFence();
    }
    
    @Override
    public void fullFence() {
        ((jdk.internal.misc.Unsafe) junsafe).fullFence();
    }
    
    @Override
    public long objectFieldOffset(Field f) {
        return ((jdk.internal.misc.Unsafe) junsafe).objectFieldOffset(f);
    }
    
    @Override
    public long staticFieldOffset(Field f) {
        return ((jdk.internal.misc.Unsafe) junsafe).staticFieldOffset(f);
    }
    
    @Override
    public Object staticFieldBase(Field f) {
        return ((jdk.internal.misc.Unsafe) junsafe).staticFieldBase(f);
    }
    
    @Override
    public boolean shouldBeInitialized(Class<?> c) {
        return ((jdk.internal.misc.Unsafe) junsafe).shouldBeInitialized(c);
    }
    
    @Override
    public void ensureClassInitialized(Class<?> c) {
        ((jdk.internal.misc.Unsafe) junsafe).ensureClassInitialized(c);
    }
    
    @Override
    public int arrayBaseOffset(Class<?> arrayClass) {
        return ((jdk.internal.misc.Unsafe) junsafe).arrayBaseOffset(arrayClass);
    }
    
    @Override
    public int arrayIndexScale(Class<?> arrayClass) {
        return ((jdk.internal.misc.Unsafe) junsafe).arrayIndexScale(arrayClass);
    }
    
    @Override
    public <T> Class<T> defineClass(String name, byte[] bytes, int off, int len, ClassLoader loader,
                                    ProtectionDomain protectionDomain) {
        return (Class<T>) ((jdk.internal.misc.Unsafe) junsafe).defineClass(name,
                                                                           bytes,
                                                                           off,
                                                                           len,
                                                                           loader,
                                                                           protectionDomain);
    }
    
    @Override
    public Object allocateInstance(Class<?> clazz) throws InstantiationException {
        return ((jdk.internal.misc.Unsafe) junsafe).allocateInstance(clazz);
    }
}
